/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import fetch from '@system.fetch';
import storage from '@system.storage';
import {HuaweiIdAuthParamsHelper, HuaweiIdAuthManager, Scope} from '@hmscore/hms-jsb-account'
import config from '../../../../js/connection/common/config.js'
import constants from '../../../../js/connection/common/scripts/constants.js'

const TAG = 'JS/Connection/HuaweiAccount: '
const TokenURL = 'https://oauth-login.cloud.huawei.com/oauth2/v3/token'
const REFRESH_TOKEN_EXPIRE_DATE = 180

const ResultCode = {
  AUTHORIZATION_CODE_EFFECT: '11001',
  ACCESS_TOKEN_EFFECT: '11002',
  REFRESH_TOKEN_EFFECT: '11003',
}

function formBody(body) {
  return Object.keys(body).map(property => {
    var encodedKey = encodeURIComponent(property);
    var encodedValue = encodeURIComponent(body[property]);
    return encodedKey + '=' + encodedValue
  }).join('&')
}

function signInError(error) {
  switch (error.errCode) {
    case constants.loginErrorCode.SIGN_IN_CANCELLED:
      break;
    case constants.loginErrorCode.SIGN_IN_AUTH:
    case constants.loginErrorCode.FAILED:
      error.showMsg = 'strings.public_logged_out'
      break;
    case constants.loginErrorCode.SIGN_IN_NETWORK_ERROR:
    case constants.loginErrorCode.SIGN_IN_AUTH_SERVER_FAILED:
      error.showMsg = 'strings.public_network_error'
      break;
    default:
      error.showMsg = 'strings.public_system_error'
  }
  return error
}

function RESTError(e) {
  // 错误码: https://developer.huawei.com/consumer/cn/doc/development/HMSCore-References/server-error-codes-0000001062371380
  let data = JSON.parse(e.data)
  if (e.code == 400) {
    if (data.error == 6 || data.error == 102) {
      data.errorCode = ResultCode.ACCESS_TOKEN_EFFECT
    } else if (data.error == 1101 && (data.sub_error == 20155 || data.sub_error == 20156)) {
      data.errorCode = ResultCode.AUTHORIZATION_CODE_EFFECT
    } else if (data.error == 1203 && (data.sub_error == 11205 || data.sub_error == 31204)) {
      data.errorCode = ResultCode.REFRESH_TOKEN_EFFECT
    }
  }
  return data
}

function resolveAuthId(authId) {
  let resultObj = {}
  resultObj.allScopeGranted = HuaweiIdAuthManager.containScopes(authId, config.mandatoryScopeList);
  resultObj.authCode = authId.serverAuthCode;
  resultObj.unionId = authId.unionId;
  resultObj.openId = authId.openId;
  return resultObj;
}

function performSignIn() {
  return new Promise((resolve, reject) => {
    let scopes = config.requireScopeList.map(scopeUri => new Scope(scopeUri))
    let signInOption = new HuaweiIdAuthParamsHelper().setId()
      .setProfile()
      .setScopeList(scopes)
      .setAuthorizationCode()
      .build();
    HuaweiIdAuthManager.getAuthApi()
      .getSignInIntent(signInOption)
      .then(resolve)
      .catch(result => reject(signInError(result)));
  })
}
function performSilentSignIn() {
  return new Promise((resolve, reject) => {
    let scopes = config.mandatoryScopeList.map(scopeUri => new Scope(scopeUri));
    let signInOption = new HuaweiIdAuthParamsHelper()
      .setId()
      .setProfile()
      .setScopeList(scopes)
      .setAuthorizationCode()
      .build();
    HuaweiIdAuthManager.getAuthApi()
      .silentSignIn(signInOption)
      .then(resolve)
      .catch(result => reject(signInError(result)));
  })
}

function getAccessToken(authId) {
  return new Promise((resolve, reject) => {
    let loginResult = resolveAuthId(authId);
    let body = {
      grant_type: 'authorization_code',
      client_id: config.clientId,
      client_secret: config.clientSecret,
      redirect_uri: config.redirectUri,
      code: loginResult.authCode
    }
    fetch.fetch({
      url: TokenURL,
      header: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      data: formBody(body),
      method: 'POST',
      success: (rawTokenResult) => {
        let tokenResult = JSON.parse(rawTokenResult.data);
        loginResult.accessToken = tokenResult.access_token;
        loginResult.refreshToken = tokenResult.refresh_token;
        saveRefreshToken(loginResult.refreshToken)
        resolve(loginResult);
      },
      fail: (data) => {
        reject(RESTError(data));
      }
    });
  });
}

function saveRefreshToken(token) {
  console.info(TAG + 'Start saving Refresh Token data.');
  let date = new Date();
  date.setDate(date.getDate() + REFRESH_TOKEN_EXPIRE_DATE);
  // Refresh token is valid in 180 days. However logout/auth cancel may revoke the token.
  storage.set({
    key: 'rt_expire_timestamp',
    value: date.getTime().toString(),
    success: () => {
      console.debug(TAG + 'Refresh Token expiration is saved.');
    },
    fail: (data, code) => {
      console.error(TAG + 'Save Refresh Token expiration failed, error code:' + code + ', data: ' + data);
    }
  });
  storage.set({
    key: 'refresh_token',
    value: token,
    success: () => {
      console.debug(TAG + 'Refresh Token is saved.');
    },
    fail: (data, code) => {
      console.error(TAG + 'Save Refresh Token failed, error code:' + code + ', data: ' + data);
    }
  });
}

export default {
  signIn() {
    return performSignIn().then(getAccessToken);
  },
  silentSignIn() {
    return performSilentSignIn().then(getAccessToken);
  },
  refreshAccessToken(successCallback, failCallback) {
    storage.get({
      key: 'refresh_token',
      success: (token) => {
        let body = {
          grant_type: 'refresh_token',
          client_id: config.clientId,
          client_secret: config.clientSecret,
          refresh_token: token
        }
        fetch.fetch({
          url: TokenURL,
          header: {
            'Content-Type': 'application/x-www-form-urlencoded'
          },
          data: formBody(body),
          method: "POST",
          success: (data) => {
            successCallback(JSON.parse(data.data));
          },
          fail: (data, code) => failCallback(RESTError(data), code)
        });
      },
      fail: (data, code) => {
        console.error(TAG + 'Refresh access token data failed, error code:' + code + ', data: ' + data);
      }
    });
  }
}